from typing import List
from copy import deepcopy

from distance import euclid_dis


class Clustering:
    def __init__(self) -> None:
        # 聚类内的样本集和样本均值
        self.samples = []
        self.m = None
    

    def __repr__(self) -> str:
        return str(self.samples)
    

    def add(self, s: List) -> object:
        '''
        向聚类中添加样本
        '''
        self.samples.append(deepcopy(s))
        return self
    

    def calc_m(self) -> object:
        '''
        重新计算聚类的样本均值
        '''
        dim = len(self.samples[0])
        self.m = []
        for d in range(dim):
            self.m.append(sum([self.samples[i][d] for i in range(len(self.samples))])/len(self.samples))
        return self


    def get_m(self) -> List:
        '''
        返回 聚类的样本均值
        '''
        if self.m is None:
            self.calc_m()
            
        return self.m

    def has_sample(self, s: List) -> bool:
        '''
        判断一个样本是否在该聚类中
        输入 一个样本
        返回 bool值
        '''
        return s in self.samples


    def merge(self, clu: object) -> object:
        '''
        将另一个聚类合并入当前聚类
        输入 另一个聚类
        返回 当前聚类
        '''
        self.samples += deepcopy(clu.samples)
        return self


    def is_equal(self, clu: object) -> bool:
        '''
        判断给定聚类内的样本是否与当前样本相等
        '''
        return self.samples == clu.samples


    def largest_dis_in_clu(self, get_dis) -> int:
        '''
        使用给定的距离算法计算最长类内距离
        返回 聚类内最远的两个样本的距离
        '''
        max_dis = 0
        for i,s1 in enumerate(self.samples):
            for j,s2 in enumerate(self.samples):
                if i == j:
                    continue
                max_dis = max(max_dis, get_dis(s1,s2))
        return max_dis


    def smallest_dis_between_clu(self, clu: object, get_dis) -> int:
        '''
        使用给定的距离算法计算最短类间距离
        返回 两个聚类间最近的两个样本的距离
        '''        
        min_dis = float('inf')
        for s1 in self.samples:
            for s2 in clu.samples:
                min_dis = min(min_dis,get_dis(s1,s2))
        return min_dis


    def inner_variance(self) -> int:
        '''
        计算当前聚类的离散度（聚类内方差）
        '''
        self.calc_m()
        return (sum([euclid_dis(s,self.m)**2  for s in self.samples])/len(self.samples))**(1/2)


    def similarity(self, clu: object) -> int:
        '''
        计算当前聚类与给定聚类的相似度
        返回 R = (s1 + s2) / d
        @s1 @s2 当前聚类和给定聚类的离散度 @d 两个聚类的样本均值距离 
        '''
        self.calc_m()
        clu.calc_m()
        if self.m == clu.m:
            return float('inf')
        return (self.inner_variance() + clu.inner_variance()) / euclid_dis(self.m, clu.m)


# for debug
# c = Clustering().add([9,4]).add([1,2]).calc_m()
# print(c.get_m())


            